#pragma once

#include <stdio.h>
#include <string.h>
#include <Windows.h>

const static char* g_s_szCh64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-";

static char* Base64Encode(BYTE* pbySrc, int nSrcLen)
{
    int     nSrcBufLen      = nSrcLen;
    BOOL    bMalloc         = false;
    int     nSrcIndex       = 0;
    int     nDstIndex       = 0;
    BYTE*   pbySrcBuffer    = NULL;
    char*   pszDstBuffer    = NULL;

    nSrcBufLen = (nSrcLen + 2) / 3 * 3;
    pbySrcBuffer = (BYTE*)malloc(nSrcBufLen);
    memcpy(pbySrcBuffer, pbySrc, nSrcLen);
    for (int i = 0; i < (nSrcLen + 2) / 3 * 3 - nSrcLen; i++)
    {
        pbySrcBuffer[nSrcLen + i] = 0;
    }

    pszDstBuffer = (char*)malloc(nSrcBufLen * 4 / 3 + 1);
    
    for (nSrcIndex = 0, nDstIndex = 0; nSrcIndex < nSrcLen / 3 * 3; nSrcIndex += 3, nDstIndex += 4)
    {
        pszDstBuffer[nDstIndex]     = (char) ((pbySrcBuffer[nSrcIndex]     & 0xFC) >> 2);
        pszDstBuffer[nDstIndex + 1] = (char)(((pbySrcBuffer[nSrcIndex]     & 0x03) << 4) +
                                             ((pbySrcBuffer[nSrcIndex + 1] & 0xF0) >> 4));
        pszDstBuffer[nDstIndex + 2] = (char)(((pbySrcBuffer[nSrcIndex + 1] & 0x0F) << 2) +
                                             ((pbySrcBuffer[nSrcIndex + 2] & 0xC0) >> 6));
        pszDstBuffer[nDstIndex + 3] = (char)  (pbySrcBuffer[nSrcIndex + 2] & 0x3F);
    }

    if (nSrcLen % 3 == 1)
    {
        pszDstBuffer[nDstIndex]     = (char) ((pbySrcBuffer[nSrcIndex]     & 0xFC) >> 2);
        pszDstBuffer[nDstIndex + 1] = (char)(((pbySrcBuffer[nSrcIndex]     & 0x03) << 4) +
                                             ((pbySrcBuffer[nSrcIndex + 1] & 0xF0) >> 4));
        pszDstBuffer[nDstIndex + 2] = 64;
        pszDstBuffer[nDstIndex + 3] = 64;
    }
    else if (nSrcLen % 3 == 2)
    {
        pszDstBuffer[nDstIndex]     = (char) ((pbySrcBuffer[nSrcIndex]     & 0xFC) >> 2);
        pszDstBuffer[nDstIndex + 1] = (char)(((pbySrcBuffer[nSrcIndex]     & 0x03) << 4) +
                                             ((pbySrcBuffer[nSrcIndex + 1] & 0xF0) >> 4));
        pszDstBuffer[nDstIndex + 2] = (char)(((pbySrcBuffer[nSrcIndex + 1] & 0x0F) << 2) +
                                             ((pbySrcBuffer[nSrcIndex + 2] & 0xC0) >> 6));
        pszDstBuffer[nDstIndex + 3] = 64;
    }

    for (int nSrcIndex = 0; nSrcIndex < nSrcBufLen * 4 / 3; nSrcIndex++)
    {
        pszDstBuffer[nSrcIndex] = g_s_szCh64[pszDstBuffer[nSrcIndex]];
    }

    pszDstBuffer[nSrcBufLen * 4 / 3] = '\0';

    return pszDstBuffer;
}

static BYTE* Base64Decode(size_t* uOutLen, const char* pszSrc)
{
    int             nSrcLen         = strlen(pszSrc);
    int             nLeft           = 0;    
    BYTE*           pbyDest         = NULL;
    const char*     pszIndex        = NULL;
    char*           pszSrcBuffer    = NULL;

    XY_FAILED_JUMP(pszSrc && nSrcLen);

    pszSrcBuffer = (char*)malloc(nSrcLen + 1);

    memcpy(pszSrcBuffer, pszSrc, nSrcLen + 1);

    for (int i = 0; i < nSrcLen; i++)
    {
        pszIndex = strchr(g_s_szCh64, (int)pszSrcBuffer[i]);
        XYLOG_FAILED_JUMP(pszIndex);
        
        if (*pszIndex == '-')
        {
            nSrcLen = i;
            break;
        }

        pszSrcBuffer[i] = pszIndex - (char*)g_s_szCh64;
    }

    if(nSrcLen % 4)
    {
        nLeft = nSrcLen % 4 - 1;
    }

    *uOutLen = nSrcLen / 4 * 3 + nLeft;

    pbyDest = (BYTE*)malloc(*uOutLen);


    for (int i = 0, j = 0; i < nSrcLen; i += 4, j += 3)
    {
        if (pszSrcBuffer[i + 2] == '-')
        {
            pbyDest[j] = (pszSrcBuffer[i] << 2) + ((pszSrcBuffer[i + 1] & 0x30) >> 4);
            break;
        }
        else if (pszSrcBuffer[i + 3] == '-')
        {
            pbyDest[j]      =   (pszSrcBuffer[i]             << 2) + ((pszSrcBuffer[i + 1] & 0x30) >> 4);
            pbyDest[j + 1]  = (((pszSrcBuffer[i + 1] & 0x0F) << 4) + ((pszSrcBuffer[i + 2] & 0x3C) >> 2));
            break;
        }

        pbyDest[j]      =   (pszSrcBuffer[i]             << 2) + ((pszSrcBuffer[i + 1] & 0x30) >> 4);
        pbyDest[j + 1]  = (((pszSrcBuffer[i + 1] & 0x0F) << 4) + ((pszSrcBuffer[i + 2] & 0x3C) >> 2));
        pbyDest[j + 2]  =  ((pszSrcBuffer[i + 2] & 0x03) << 6) +   pszSrcBuffer[i + 3];
    }

Exit0:
    XY_FREE(pszSrcBuffer);
    return pbyDest;
}